package com.demo.es.service.impl;

import com.demo.es.common.Page;
import com.demo.es.pojo.dto.UserDTO;
import com.demo.es.pojo.request.QueyUserRequest;
import com.demo.es.pojo.response.PageInfoResponse;
import com.demo.es.service.ElasticSearchService;
import com.demo.es.service.constant.ElasticSearchConstant;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.BucketOrder;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.*;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @author 李炯 elasticsearch业务层
 * @description: DOTO
 * @program: elastic-demo
 * @date 2020/06/01
 **/
@Service
public class ElasticSearchServiceImpl implements ElasticSearchService {
    /**
     * ES高级客户端
     */
    @Autowired
    private RestHighLevelClient restHighLevelClient;


    @Override
    public PageInfoResponse<List<UserDTO>> queryHighClientSearch(Page<QueyUserRequest> userRequest) throws IOException {
        //创建索引查询对象
        SearchRequest request = new SearchRequest(ElasticSearchConstant.INDEX_GOODES);
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        //计算开始查询条数
        int from = userRequest.getPageCount() * userRequest.getPageIndex();
        //分页查询
        sourceBuilder.from(from).size(userRequest.getPageCount());
        //需要展示的字段数组
        String[] include = {};
        //排除不需要展示的字段数组
        String[] exclude = {"image", "title"};
        sourceBuilder.fetchSource(include, exclude);
        //联合查询对象
        QueryBuilder queryBuilder = QueryBuilders.boolQuery()
                .must(QueryBuilders.termsQuery("goodsType", "键盘"))
                .must(QueryBuilders.termsQuery("price","299"))
                .must(QueryBuilders.termsQuery("address","贵州"));
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.must(QueryBuilders.termsQuery("goodsType","手机"));
        boolQueryBuilder.mustNot(QueryBuilders.termsQuery("goodsName","华为"));
        BoolQueryBuilder boolQueryBuilder1 = QueryBuilders.boolQuery();

        boolQueryBuilder1.should(queryBuilder).should(boolQueryBuilder);
        if (1 == 2) {
            sourceBuilder.query(boolQueryBuilder);
            //设置高亮
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            highlightBuilder.preTags("<tag>");//设置前缀
            highlightBuilder.postTags("<tag>");//设置后缀
            highlightBuilder.fields().add(new HighlightBuilder.Field("userName"));
            //设置高亮
            sourceBuilder.highlighter(highlightBuilder);
        }
        //构建查询条件
        request.source(sourceBuilder.query(boolQueryBuilder1));
        querySearch(request);
        return null;

    }

    @Override
    public PageInfoResponse<List<UserDTO>> queryHighUserList(Page<QueyUserRequest> userRequest) throws IOException {
        //创建索引查询对象
        SearchRequest request = new SearchRequest(ElasticSearchConstant.INDEX_USER);
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        //分组命名
        TermsAggregationBuilder field = AggregationBuilders.terms("group_goodsType").field("goodsType");
        //计算该字段的最高工资
        MaxAggregationBuilder maxPrice = AggregationBuilders.max("max_price").field("price");
        //计算该字段的最低工资
        MinAggregationBuilder minPrice = AggregationBuilders.min("min_price").field("price");
        //分组后计算最高工资,计算最低工资
        sourceBuilder.aggregation(field.subAggregation(maxPrice).subAggregation(minPrice)).aggregation(minPrice);
        request.source(sourceBuilder);
        SearchResponse searchResponse = restHighLevelClient.search(request, RequestOptions.DEFAULT);
        //获取分组后的数据
        ParsedStringTerms groupGoodsType = (ParsedStringTerms) searchResponse.getAggregations().asMap().get("group_goodsType");
        //循环结果
        for (Terms.Bucket bucket : groupGoodsType.getBuckets()) {
            Object key = bucket.getKey();
            long docCount = bucket.getDocCount();
            System.out.print("按照商品类型划分:" + key.toString() + "有:" + docCount + "件");
            ParsedMax maxPriceParsed = (ParsedMax) bucket.getAggregations().asMap().get("max_price");
            ParsedMax minPriceParsed = (ParsedMax) bucket.getAggregations().asMap().get("min_price");
            System.out.println(key.toString() + "最高价格:" + maxPriceParsed.getValue());
            System.out.println(key.toString() + "最低价格:" + minPriceParsed.getValue());
        }
        ParsedMin parsedMax = (ParsedMin) searchResponse.getAggregations().asMap().get("min_salary");
        System.out.println("最低商品价格为" + parsedMax.getValue());
        return null;

    }

    @Override
    public PageInfoResponse<List<UserDTO>> queryAvgUserList(Page<QueyUserRequest> userRequest) throws IOException {
        //创建索引查询对象
        SearchRequest request = new SearchRequest(ElasticSearchConstant.INDEX_GOODES);
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        //指定按照goodsType字段分组,并将分组名称指定为group_goodsType(可随机命名),在后期会用到这个分组名
        TermsAggregationBuilder goodsTypeField = AggregationBuilders.terms("group_goodsType").field("goodsType");
        //计算按照price字段计算平均值,并指定计算名称avg_salary(可随机),后期会用到分组名
        AvgAggregationBuilder priceField = AggregationBuilders.avg("avg_price").field("price");
        //按照商品类型分组后,(注意计算平均值在分组后新增的参数)计算每个分组的平均值,并且根据前期命名avg_price进行排序  true为正,false为倒叙
        sourceBuilder.aggregation(goodsTypeField.subAggregation(priceField).order(BucketOrder.aggregation("avg_price", true)));
        request.source(sourceBuilder);
        //利用高级客户端进行search查询
        SearchResponse search = restHighLevelClient.search(request, RequestOptions.DEFAULT);
        //获取分组后的统计对象,根据前期命名的分组名进行获取group_goodsType
        ParsedStringTerms stringTerms = (ParsedStringTerms) search.getAggregations().asMap().get("group_goodsType");
        for (Terms.Bucket bucket : stringTerms.getBuckets()) {
            Object key = bucket.getKey();
            long docCount = bucket.getDocCount();
            System.out.print("按照地区划分统计结果:" + key.toString() + "->商品类型为:" + docCount);
            ParsedAvg salaryMax = (ParsedAvg) bucket.getAggregations().asMap().get("avg_price");
            System.out.println(key + "平均工资为:" + salaryMax.getValue());
        }
//        ParsedAvg salaryMax = (ParsedAvg) search.getAggregations().asMap().get("avg_salary");
        return null;

    }

    @Override
    public PageInfoResponse<List<UserDTO>> queryHighClient(Page<QueyUserRequest> userRequest) throws IOException {
        //创建索引查询对象
        SearchRequest request = new SearchRequest(ElasticSearchConstant.INDEX_GOODES);
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.from(countPageFrom(userRequest)).size(userRequest.getPageCount());
        String[] include = {};
        String[] exclude = {"image"};
        sourceBuilder.fetchSource(include, exclude);
        //联合查询对象
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //term绝对查询.所选字段的value不会分词 在底层直接进行匹配
        boolQueryBuilder.must(QueryBuilders.termQuery("goodsType", "手机"));
        boolQueryBuilder.mustNot(QueryBuilders.matchQuery("goodsName","华为"));
        //构建查询条件
        request.source(sourceBuilder.query(boolQueryBuilder));
        querySearch(request);
        return null;
    }

    /**
      * @Description 利用高级客户端查询并开始打印消息
      * @Author: 李炯
      * @Date: 2020/6/3 10:51
      * @Param: [request]
      * @Return: void
      * @Exception:
      **/
    private void querySearch(SearchRequest request) throws IOException {
        //开始查询
        SearchResponse searchResponse = restHighLevelClient.search(request, RequestOptions.DEFAULT);
        SearchHits hits = searchResponse.getHits();
        for (SearchHit hit : hits.getHits()) {
            //获取可提供高亮字段
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            for (Map.Entry<String, HighlightField> stringHighlightFieldEntry : highlightFields.entrySet()) {
                String key = stringHighlightFieldEntry.getKey();
                Object value = stringHighlightFieldEntry.getValue();
                System.out.print("[" + key + ":" + value + "]");
            }
            //获取数据
            Map<String, Object> sourceAsMap = hit.getSourceAsMap();
            for (Map.Entry<String, Object> stringObjectEntry : sourceAsMap.entrySet()) {
                String key = stringObjectEntry.getKey();
                Object value = stringObjectEntry.getValue();
                System.out.print("[" + key + ":" + value + "]");
            }
            System.out.println();
        }
    }

    /**
      * @Description 计算每页查询从几行开始
      * @Author: 李炯
      * @Date: 2020/6/3 11:01
      * @Param: []
      * @Return: java.lang.Integer
      * @Exception:
      **/
    private Integer countPageFrom(Page userRequest){
        //计算开始查询条数
        return userRequest.getPageCount() * userRequest.getPageIndex();
    }
}
